/**
 * 
 */
package com.monte.export.excel;

import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.log4j.Logger;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import com.monte.export.Configure;
import com.monte.export.DataPage;
import com.monte.export.ExportDataSource;

/**
 * 大数据Excel数据导出
 * @author fengmengyue
 *
 */
public class LargeDataExcelExportor<T> extends ExcelDataExportor<T> {
	private Logger logger = Logger.getLogger(LargeDataExcelExportor.class);
	
	private SXSSFWorkbook workbook;

	public LargeDataExcelExportor(ExportDataSource<T> dataSource,OutputStream outputStream,Configure configure) {
		super(dataSource, outputStream,configure);
	}

	@Override
	protected void startExport() {
		logger.debug("导出开始。");
		long startTime = System.currentTimeMillis();
		workbook = new SXSSFWorkbook();
		DataPage<T> dataPage = new DataPage<T>();
		dataPage.setPageSize(configure.getPageSize());
		dataPage.setCurrentPage(dataPage.getNextPage());
		dataPage = getDataPage(dataPage);
		int totalCount = dataPage.getTotalCount();
		int sheetSize = totalCount == 0?1:(totalCount % configure.getRowsOfSheet())==0?(totalCount / configure.getRowsOfSheet()):(totalCount / configure.getRowsOfSheet() + 1);
		Sheet firstSheet = workbook.createSheet(configure.getSheetName());
		CountDownLatch countDownLatch = new CountDownLatch(sheetSize);
		logger.debug("启动线程数：" + sheetSize);
		int rownum = writeHead(firstSheet);
		SheetExportor thread = new SheetExportor(firstSheet, rownum, 0,countDownLatch);
		thread.start();
		for(int i = 1; i < sheetSize; i++){
			thread = new SheetExportor(workbook.createSheet(configure.getSheetName() + i), 0, configure.getRowsOfSheet()*i,countDownLatch);
			thread.start();
		}
		try {
			countDownLatch.await();
			logger.debug("取数和写入excel耗时：" + (System.currentTimeMillis() - startTime));
			startTime = System.currentTimeMillis();
			workbook.write(outputStream);
			logger.debug("IO发送耗时：" + (System.currentTimeMillis() - startTime));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	class SheetExportor extends Thread{
		private Sheet sheet;
		private int startrow;
		private int dataStartIndex;
		private CountDownLatch countDownLatch;
		private SheetExportor(Sheet sheet,int startrow,int dataStartIndex,CountDownLatch countDownLatch){
			this.sheet = sheet;
			this.startrow = startrow;
			this.dataStartIndex = dataStartIndex;
			this.countDownLatch = countDownLatch;
		}
		@Override
		public void run() {
			DataPage<T> dataPage = new DataPage<T>();
			dataPage.setPageSize(configure.getPageSize());
			int rownum = startrow;
			int index = dataStartIndex+1;
			List<T> datas;
			int dataIndex;
			int nextPage = this.dataStartIndex==0?1:(this.dataStartIndex / dataPage.getPageSize() + 1);
			int lastPage = (this.dataStartIndex + configure.getRowsOfSheet())%dataPage.getPageSize()==0?((this.dataStartIndex + configure.getRowsOfSheet())/dataPage.getPageSize()):((this.dataStartIndex + configure.getRowsOfSheet())/dataPage.getPageSize() + 1);
			do{
				dataPage.setCurrentPage(nextPage);
				dataPage = getDataPage(dataPage);
				datas = dataPage.getDatas();
				if(dataPage.getTotalCount() < datas.size()){
					dataPage.setTotalCount(datas.size());
				}
				if(lastPage > dataPage.getPageCount()){
					lastPage = dataPage.getPageCount();
				}
				for(int i = 0; i < datas.size(); i++){
					dataIndex = (dataPage.getCurrentPage() - 1)*dataPage.getPageSize() + i;
					if(dataIndex >= this.dataStartIndex && dataIndex < (this.dataStartIndex + configure.getRowsOfSheet())){
						writeRow(sheet,rownum,datas.get(i),index);
						rownum++;
						index++;
					}
				}
				//当还有下一页时，继续取数
				if(nextPage == dataPage.getPageCount()){
					nextPage ++;
				}else{
					nextPage = dataPage.getNextPage();
				}
			}while(nextPage <= lastPage);
			countDownLatch.countDown();
		}
	}
}
